﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="GroupViewModel.cs" company="Marcin Jastrzebski">
//   Copyright (c) Marcin Jastrzebski (marcin@marcinj.net)
// </copyright>
// <summary>
//   View model for the password group page.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace mPassRT.ViewModels.Group
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows.Input;

    using KeePassLib;
    using KeePassLib.Utility;

    using Microsoft.Practices.Unity;

    using mPassRT.Common;
    using mPassRT.Pages;
    using mPassRT.Services;

    using Windows.UI.Xaml.Media;

    /// <summary>View model for the password group page.</summary>
    public class GroupViewModel : PageViewModel
    {
        #region Fields

        /// <summary>The group.</summary>
        private PwGroup currentGroup;

        /// <summary>The database.</summary>
        private PwDatabase database;

        /// <summary>The database color.</summary>
        private Brush databaseColor;

        /// <summary>The database file path.</summary>
        private string databaseFilePath;

        /// <summary>The location.</summary>
        private string location;

        /// <summary>The title.</summary>
        private string title;

        #endregion

        #region Constructors and Destructors

        /// <summary>Initializes a new instance of the <see cref="GroupViewModel"/> class.</summary>
        public GroupViewModel()
        {
            this.Items = new ObservableCollection<object>();
        }

        #endregion

        #region Public Properties

        /// <summary>Gets the add new entry command.</summary>
        public ICommand AddNewEntryCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.AddNewEntry };
            }
        }

        /// <summary>Gets the add new group command.</summary>
        public ICommand AddNewGroupCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.AddNewGroup };
            }
        }

        /// <summary>Gets or sets the database color.</summary>
        public Brush DatabaseColor
        {
            get
            {
                return this.databaseColor;
            }

            set
            {
                this.SetProperty(ref this.databaseColor, value);
            }
        }

        /// <summary>Gets or sets the database loader.</summary>
        [Dependency]
        public IDatabaseLoader DatabaseLoader { get; set; }

        /// <summary>Gets the items.</summary>
        public ObservableCollection<object> Items { get; private set; }

        /// <summary>Gets or sets the location.</summary>
        public string Location
        {
            get
            {
                return this.location;
            }

            set
            {
                this.SetProperty(ref this.location, value);
            }
        }

        /// <summary>Gets or sets the navigator.</summary>
        [Dependency]
        public INavigator Navigator { get; set; }

        /// <summary>Gets the open command.</summary>
        public ICommand OpenCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.Open };
            }
        }

        /// <summary>Gets or sets the selected items.</summary>
        public List<ItemViewModel> SelectedItems
        {
            get
            {
                return null;
            }

            set
            {
            }
        }

        /// <summary>Gets or sets the title.</summary>
        public string Title
        {
            get
            {
                return this.title;
            }

            set
            {
                this.SetProperty(ref this.title, value);
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>Updates the page based on the currently saved state and navigation parameter.</summary>
        /// <param name="navigationParameter">The navigation parameter.</param>
        /// <param name="state">The state.</param>
        public override async void LoadState(object navigationParameter, Dictionary<string, object> state)
        {
            var navStr = navigationParameter as string;
            if (navStr == null)
            {
                return;
            }

            var parameter = SerializationUtility.DeserializeFromString<DatabaseNavigationParameter>(navStr);
            if (parameter == null)
            {
                throw new ArgumentException("Invalid navigation parameter.");
            }

            this.database = await this.DatabaseLoader.GetDatabaseAsync(parameter.FilePath);
            if (this.database == null)
            {
                this.Navigator.GoBack();
                return;
            }

            this.databaseFilePath = parameter.FilePath;
            this.currentGroup = GetGroup(this.database, parameter.ItemId);

            this.Title = this.database.Name;
            this.Location = this.GetGroupPath();
            this.DatabaseColor = new SolidColorBrush(this.database.Color);
            this.UpdateItems();
        }

        #endregion

        #region Methods

        /// <summary>Finds the group in database by group ID.</summary>
        /// <param name="database">The database.</param>
        /// <param name="groupId">The group ID.</param>
        /// <returns>The <see cref="PwGroup"/>.</returns>
        private static PwGroup GetGroup(PwDatabase database, string groupId)
        {
            if (groupId == null)
            {
                return database.RootGroup;
            }

            PwGroup group = database.RootGroup.FindGroup(new PwUuid(MemUtil.HexStringToByteArray(groupId)), true);
            return group ?? database.RootGroup;
        }

        /// <summary>Adds a new entry.</summary>
        /// <param name="obj">The parameter is not used.</param>
        private void AddNewEntry(object obj)
        {
        }

        /// <summary>Adds a new group.</summary>
        /// <param name="obj">The parameter is not used.</param>
        private void AddNewGroup(object obj)
        {
        }

        /// <summary>Gets the group path.</summary>
        /// <returns>The group path.</returns>
        private string GetGroupPath()
        {
            return object.ReferenceEquals(this.currentGroup, this.database.RootGroup)
                       ? string.Empty
                       : this.currentGroup.GetFullPath(" / ", false);
        }

        /// <summary>Opens the item.</summary>
        /// <param name="obj">The item that was opened.</param>
        private void Open(object obj)
        {
            var navParameter = new DatabaseNavigationParameter { FilePath = this.databaseFilePath };
            var group = obj as SubGroupViewModel;
            if (group != null)
            {
                navParameter.ItemId = group.Group.Uuid.ToHexString();
                this.Navigator.NavigateTo(typeof(GroupPage), navParameter);
                return;
            }

            var entry = obj as EntryViewModel;
            if (entry != null)
            {
                navParameter.ItemId = entry.Entry.Uuid.ToHexString();
                this.Navigator.NavigateTo(typeof(EntryPage), navParameter);
                return;
            }

            if (obj is NewItemViewModel)
            {
                return;
            }

            throw new ArgumentException("Unknown object type.");
        }

        /// <summary>The update items.</summary>
        private void UpdateItems()
        {
            foreach (PwGroup group in this.currentGroup.Groups)
            {
                this.Items.Add(new SubGroupViewModel(group));
            }

            foreach (PwEntry entry in this.currentGroup.Entries)
            {
                this.Items.Add(new EntryViewModel(entry));
            }

            this.Items.Add(new NewItemViewModel());
        }

        #endregion
    }
}